/* ----------------------------------------------------------------------------------------------------------------------
 * ProcessManager
 * -----------------------------------------------------------------------------------------------------------------------
 */
package pure.engine.system 
{
	import flash.display.Shape;
    import flash.events.Event;
    import flash.utils.getTimer;
	import pure.utils.DesN;
	import pure.utils.DesU;
	import pure.engine.system.ns_despair;

	use namespace ns_despair;
	
    /**
	 * @internal
	 * 
	 * ProcessManager
	 * 
	 * @param ＃Pure
     */
public class ProcessManager
{

	public static const TICKS_PER_SECOND:int = 30;	// 每秒绝望次数

	public static const TICK_RATE_MS:Number = 1.0 / Number(TICKS_PER_SECOND) * 1000;	// 绝望间隔(ms)

	public static const MAX_TICKS_PER_FRAME:int = 5;	// 每帧最大绝望次数
	
	protected static var shape:Shape = new Shape();  // 逐帧侦听
	

	
	protected var animatedObjects:Vector.<ProcessObject> = new Vector.<ProcessObject>();	// 动画对象列表
	
	protected var tickedObjects:Vector.<ProcessObject> = new Vector.<ProcessObject>();	// 绝望对象列表
	
	//protected var cachedProcessObjects:Array = [];
	
	
	/*cache*/
	
	protected var started:Boolean;	// 是否已启动
	
	protected var _virtualTime:int = 0.0;	// 累计总时间

	protected var _timeScale:Number = 1.0;	// 时间速率倍数(可改变运行速度)
	
	protected var lastTime:int = -1.0;
	
	protected var elapsed:Number = 0.0;

	protected var needPurgeEmpty:Boolean;	// 需要净化列表空位
	
	protected var duringAdvance:Boolean;	// 正处于逐帧推进中

	
	
	
	/**
	 * 启动
	 */
	public function start():void
	{
		if (started) 
		{
			trace("start", "The ProcessManager is already started.");
			return;
		}
		lastTime = -1.0;
		elapsed = 0.0;
		shape.addEventListener(Event.ENTER_FRAME, ____onEnterFrame, false, 10, true);
		started = true;
	}
	
	
	/**
	 * 停止
	 */
	public function stop():void
	{
		if (!started)
		{
			trace("stop", "The ProcessManager isn't started.");
			return;
		}
		started = false;
		shape.removeEventListener(Event.ENTER_FRAME, ____onEnterFrame);
	}


	/**
	 * 添加动画对象
	 * @param object
	 * @param priority
	 */
	public function addFrameObject(object:IFrameObject, priority:Number = 0.0):void
	{
		addObject(object, priority, animatedObjects);
	}
	
	/**
	 * 添加绝望对象
	 * @param object
	 * @param priority
	 */
	public function addTickObject(object:ITickObject, priority:Number = 0.0):void
	{
		addObject(object, priority, tickedObjects);
	}

	
	/**
	 * 移除动画对象
	 * @param object
	 */
	public function removeFrameObject(object:IFrameObject):void
	{
		removeObject(object, animatedObjects);
	}
	
	/**
	 * 移除绝望对象
	 * @param object
	 */
	public function removeTickedObject(object:ITickObject):void
	{
		removeObject(object, tickedObjects);
	}
	
	/**
	 * 强制前进一段时间
	 * <TODO> 仅用于单元测试
	 * @param amount	前进时间
	 */
	public function testAdvance(amount:Number):void
	{
		advance(amount * _timeScale, true);
	}
	
	/**
	 * Forces the process manager to seek its virtualTime by the specified amount.
	 * This moves virtualTime without calling advance and without processing ticks or frames.
	 * WARNING: USE WITH CAUTION AND ONLY IF YOU REALLY KNOW THE CONSEQUENCES!
	 */
	public function seek(amount:Number):void
	{
		_virtualTime += amount;
	}
	

	
		
	// 时间速率倍数(可改变运行速度)
	public function get timeScale():Number { return _timeScale;}
	public function set timeScale(value:Number):void { _timeScale = value; }

	
	/**
	 * The amount of time that has been processed by the process manager. This does
	 * take the time scale into account. Time is in milliseconds.
	 */
	public function get virtualTime():Number { return _virtualTime; }

	// 是否正在绝望
	public function get isTicking():Boolean { return started; }
		
	// 侦听对象总数
	private function get listenerCount():int { return tickedObjects.length + animatedObjects.length; }
	
	// 单例
	public static function get instance():ProcessManager 
	{
		return _instance || (_instance = new ProcessManager());
	}
	private static var _instance:ProcessManager
	
	
	
	
	
	/* ----------------------------------------------------------------------------------------------------------------------
	 * ★★★★★★★★★★★★★★★★  private  ★★★★★★★★★★★★★★★★
	 * ----------------------------------------------------------------------------------------------------------------------
	 */
	
	

	 
	 
	/**
	 * 通过优先级添加对象
	 * @param object		对象
	 * @param priority		优先级
	 * @param list			列表
	 */
	private function addObject(object:*, priority:Number, list:Vector.<ProcessObject>):void
	{
		//if (duringAdvance)
		//{
			//cachedProcessObjects.push(
		//}
		
		if (!started) start();
		var position:int = -1;
		var len:int = list.length;
		for (var i:int = 0; i < len; i++)
		{
			if(!list[i]) continue;
			if (list[i].listener == object) {
				trace("AddProcessObject", "This object has already been added to the process manager.");
				return;
			}
			if (list[i].priority < priority) {
				position = i;
				break;
			}
		}
		
		// 将对象转换为统一的ProcessObject对象，再添加
		var processObject:ProcessObject = new ProcessObject();
		processObject.listener = object;
		processObject.priority = priority;
		
		if (position < 0 || position >= len) {
			list.push(processObject);
		}
		else {
			list.splice(position, 0, processObject);
		}
	}
	
	/**
	 * 移除对象
	 * @param object		对象
	 * @param list			列表
	 */
	private function removeObject(object:*, list:Vector.<ProcessObject>):void 
	{
		var len:int = list.length;
		for (var i:int = 0; i < len; i++) {
			if (!list[i]) continue;
			
			if (list[i].listener == object) {
				if (duringAdvance)
				{
					list[i] = null;
					needPurgeEmpty = true;
				}
				else {
					list.splice(i, 1);                        
				}
				return;
			}
		}	
		trace("RemoveProcessObject", "This object has not been added to the process manager.");
	}
	
	
	private function ____onEnterFrame(event:Event):void
	{
		// 当前系统时间
		var currentTime:Number = getTimer();
		if (lastTime < 0)
		{
			lastTime = currentTime;
			return;
		}
		
		var deltaTime:Number = Number(currentTime - lastTime) * _timeScale;
		advance(deltaTime);
		lastTime = currentTime;
	}
	
	
	/**
	 * 逐帧推进
	 * @param deltaTime			间隔时间(ms)
	 * @param suppressSafety	禁止安全模式
	 */
	private function advance(deltaTime:Number, suppressSafety:Boolean = false):void
	{
		elapsed += deltaTime;

		var lenT:int;
		var lenA:int;
		
		var t:int = getTimer();
		var tickCount:int = 0;
		while (elapsed >= TICK_RATE_MS && (suppressSafety || tickCount < MAX_TICKS_PER_FRAME))
		{
			//======================
			// Tick
			//======================

			// 处理绝望对象
			duringAdvance = true;
			lenT = tickedObjects.length;
			for (var j:int = 0; j < lenT; j++) 
			{
				var object:ProcessObject = tickedObjects[j] as ProcessObject;
				if(!object) continue;
				(object.listener as ITickObject).onTick(TICK_RATE_MS);
			}
			duringAdvance = false;
			
			// Update virtual time by subtracting from accumulator.
			_virtualTime += TICK_RATE_MS;
			elapsed -= TICK_RATE_MS;
			tickCount++;
		}
		
		// 非安全模式下，警告防止死循环。
		if (tickCount >= MAX_TICKS_PER_FRAME && !suppressSafety) 
		{
			trace("advance", "Exceeded maximum number of ticks for frame (" + elapsed.toFixed() + "ms dropped) .");
			elapsed = 0;
		}

		// 确保不会落后太远，预防短时间内帧率降低造成影响。
		elapsed = DesN.clamp(elapsed, 0, 300);            
		
		
		
		//======================
		// Frame
		//======================

		// 处理动画对象
		duringAdvance = true;
		lenA = animatedObjects.length;
		for (var i:int = 0; i < lenA; i++)
		{
			var animatedObject:ProcessObject = animatedObjects[i] as ProcessObject;
			if (!animatedObject) continue;
			(animatedObject.listener as IFrameObject).onFrame(deltaTime);
		}
		duringAdvance = false;

		// 净化列表空位
		if (needPurgeEmpty)
		{
			needPurgeEmpty = false;

			for (j = lenA - 1; j >= 0; j--)
			{
				if (!animatedObjects[j]) animatedObjects.splice(j, 1);
			}
			for (j = lenT - 1; j >= 0 ; j--)
			{                    
				if (!tickedObjects[j]) tickedObjects.splice(j, 1);
			}
		}
	}
	

}
}

final class ProcessObject
{
    public var listener:* = null;
    public var priority:Number = 0.0;
}
